package com.bestcem.xm.member.handler.member;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bestcem.xm.common.core.constant.AppRequestErrorCode;
import com.bestcem.xm.common.core.constant.AppResourceErrorCode;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.domain.web.XmResultJson;
import com.bestcem.xm.common.core.sql.ConditionDataConverter;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.common.core.uitls.EnumUtil;
import com.bestcem.xm.member.controller.param.*;
import com.bestcem.xm.member.dao.MemberDao;
import com.bestcem.xm.member.dao.SourceCardDao;
import com.bestcem.xm.member.entity.mongo.ExtendFieldValue;
import com.bestcem.xm.member.entity.mongo.Member;
import com.bestcem.xm.member.entity.mysql.LabelValueId;
import com.bestcem.xm.member.entity.pojo.MemberDO;
import com.bestcem.xm.member.enums.ExtendFieldTypeEnum;
import com.bestcem.xm.member.enums.MemberTypeEnum;
import com.bestcem.xm.member.enums.NewMemberStatusEnum;
import com.bestcem.xm.member.grpc.client.XmMemberClient;
import com.bestcem.xm.member.grpc.service.param.member.MemberFieldParam;
import com.bestcem.xm.member.grpc.service.param.member.MemberLabelParam;
import com.bestcem.xm.member.mq.message.MemberMessage;
import com.bestcem.xm.member.service.GroupService;
import com.bestcem.xm.member.service.SequenceService;
import com.bestcem.xm.member.service.dto.OrganizationDTO;
import com.bestcem.xm.member.util.CheckUtil;
import com.bestcem.xm.member.util.FieldUtil;
import com.bestcem.xm.member.util.MemberUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static com.bestcem.xm.member.constant.Constants.EMPLOYEE_STATUS_MAP;
import static com.bestcem.xm.member.enums.MemberTypeEnum.INDIVIDUAL;
import static com.bestcem.xm.member.enums.MemberTypeEnum.INTERIOR;

/**
 * 联系人公共方法
 *
 * @author yf.wang <yf.wang@idiaoyan.com>
 * @version V1.0
 * @date 2021/7/16
 */
@Slf4j
public abstract class BaseMemberHandle {

    @Resource
    private MemberDao memberDao;

    @Resource
    private SequenceService sequenceService;

    @Autowired
    protected SourceCardDao sourceCardDao;

    @Autowired
    protected XmMemberClient xmMemberClient;

    @Autowired
    protected MemberUtil memberUtil;

    @Resource
    private GroupService groupService;

    /**
     * 个人联系人编号序号 key
     */
    public final static String INDIVIDUAL_MEMBER_NO_KEY = "member:memberNo_INDIVIDUAL_";

    /**
     * 机构联系人编号序号 key
     */
    public final static String INSTITUTION_MEMBER_NO_KEY = "member:memberNo_INSTITUTION_";

    /**
     * 内部联系人编号序号 key
     */
    public final static String INTERIOR_MEMBER_NO_KEY = "member:memberNo_INTERIOR_";

    /**
     * 参数校验
     *
     * @param param
     * @return
     */
    public abstract ServiceResult<String> checkParams(HttpCreateMemberParam param);

    /**
     * 保存联系人
     *
     * @param param
     * @return
     */
    public abstract ServiceResult<String> save(HttpCreateMemberParam param);

    public String getMemberNo(String orgId, Integer attribute) {
        switch (EnumUtil.getEnumByIndex(MemberTypeEnum.class, attribute)) {
            case INDIVIDUAL:
                // 个人联系人
                ServiceResult<Integer> result1 = sequenceService.getSequence(INDIVIDUAL_MEMBER_NO_KEY + orgId, 1);
                Integer seq1 = result1.getData();
                return "C" + NumberUtil.decimalFormat("00000000", seq1);
            case INSTITUTION:
                // 机构联系人
                ServiceResult<Integer> result2 = sequenceService.getSequence(INSTITUTION_MEMBER_NO_KEY + orgId, 1);
                Integer seq2 = result2.getData();
                return "B" + NumberUtil.decimalFormat("00000000", seq2);
            case INTERIOR:
                // 内部联系人
                ServiceResult<Integer> result3 = sequenceService.getSequence(INTERIOR_MEMBER_NO_KEY + orgId, 1);
                Integer seq3 = result3.getData();
                return "E" + NumberUtil.decimalFormat("00000000", seq3);
            default:
                return "";
        }
    }

    /**
     * 校验手机号与邮箱
     */
    public ServiceResult<String> checkMobileAndEmail(String mobile, String email, Integer memberType, String orgId) {
        if (StrUtil.isNotEmpty(mobile)) {
            ServiceResult<String> result = CheckUtil.checkMobilePhoneFormat(mobile);
            if (result.isSuccess()) {
                // 校验手机号的唯一性
                MemberDO member = memberDao.findByOrgIdAndFieldAndAttributeAndStatus(orgId, "phone", mobile, memberType, NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
                if (ObjectUtil.isNotEmpty(member)) {
                    return ServiceResult.<String>fail(member.getId(), "在该类型中，联系人手机号已存在")
                            .setErrorCode(String.valueOf(AppResourceErrorCode.FOUND));
                }
            } else {
                return ServiceResult.<String>fail(result.getMsg()).setErrorCode("400");
            }
        }

        if (StrUtil.isNotEmpty(email)) {
            ServiceResult<String> result = CheckUtil.checkEmailFormat(email);
            if (result.isSuccess()) {
                // 校验邮箱的唯一性
                MemberDO member = memberDao.findByOrgIdAndFieldAndAttributeAndStatus(orgId, "email", email, memberType, NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
                if (ObjectUtil.isNotEmpty(member)) {
                    return ServiceResult.<String>fail(member.getId(), "在该类型中，联系人邮箱已存在")
                            .setErrorCode(String.valueOf(AppResourceErrorCode.FOUND));
                }
            } else {
                return ServiceResult.<String>fail(result.getMsg()).setErrorCode("400");
            }
        }

        return ServiceResult.success();
    }

    public ServiceResult<String> checkUniqueValue(String mobile, String email, Integer memberType, String orgId, String outNo) {
        ServiceResult<String> result = checkMobileAndEmail(mobile, email, memberType, orgId);
        // 判断外部编号是否唯一
        if (result.isSuccess()) {
            if (StrUtil.isNotBlank(outNo)) {
                ServiceResult<String> outNoFormatResult = CheckUtil.checkOutNoFormat(outNo, memberType);
                if (outNoFormatResult.isSuccess()) {
                    MemberDO member = memberDao.findOneByOrgIdAndOutNoAndAttributeAndStatus(orgId, outNo, memberType, NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
                    if (ObjectUtil.isNotEmpty(member)) {
                        String errorMessage = memberType == INTERIOR.getIndex() ? "在该类型中，联系人员工编号已存在" : "在该类型中，联系人外部编号已存在";
                        return ServiceResult.<String>fail(member.getId(), errorMessage)
                                .setErrorCode(String.valueOf(AppResourceErrorCode.FOUND));
                    }
                } else {
                    return ServiceResult.<String>fail(outNoFormatResult.getMsg()).setErrorCode("400");
                }
            }
        }
        return result;
    }

    /**
     * 更新联系人 构造联系人实体类
     *
     * @param param
     * @return
     */
    public Member buildMember(HttpUpdateHttpMemberParam param) {
        Member member = new Member();
        BeanUtil.copyProperties(param, member);
        if (ObjectUtil.isNotEmpty(member.getBirthday())) {
            member.setBirthday(DateUtil.getMongoDate(member.getBirthday()));
        }
        fillHttpMemberField(param.getFieldValues(), member);
        fillHttpLabelValueId(param.getLabels(), member);
        member.setUpdateTime(DateUtil.getCommonDate());
        return member;
    }

    /**
     * 填充 联系人扩展字段
     *
     * @param fieldValues
     * @param member
     */
    private void fillHttpMemberField(List<HttpMemberFieldParam> fieldValues, Member member) {
        if (CollUtil.isNotEmpty(fieldValues)) {
            List<ExtendFieldValue> fieldValList = fieldValues.stream().map(f -> {
                ExtendFieldValue extendFieldValue = new ExtendFieldValue();
                extendFieldValue.setFieldId(f.getId());
                extendFieldValue.setFieldName(f.getName());
                extendFieldValue.setFieldType(f.getType());
                extendFieldValue.setFieldValue(f.getValue());
                return extendFieldValue;
            }).collect(Collectors.toList());
            member.setFieldValList(fieldValList);
        }
    }

    /**
     * 填充 联系人扩展字段
     *
     * @param fieldValues
     * @param member
     */
    private void fillMemberField(List<MemberFieldParam> fieldValues, Member member) {
        if (CollUtil.isNotEmpty(fieldValues)) {
            List<ExtendFieldValue> fieldValList = fieldValues.stream().map(f -> {
                ExtendFieldValue extendFieldValue = new ExtendFieldValue();
                extendFieldValue.setFieldId(f.getId());
                extendFieldValue.setFieldName(f.getName());
                extendFieldValue.setFieldType(f.getType());
                extendFieldValue.setFieldValue(f.getValue());
                return extendFieldValue;
            }).collect(Collectors.toList());
            member.setFieldValList(fieldValList);
        }
    }

    /**
     * 转化联系人扩展字段值实际类型
     *
     * @param fieldType   扩展字段类型index
     * @param stringValue 扩展
     * @return 实际类型
     */
    private Object parseMemberFieldValue(Integer fieldType, String stringValue) {
        ExtendFieldTypeEnum fieldTypeEnum = EnumUtil.getEnumByIndex(ExtendFieldTypeEnum.class, fieldType);
        if (fieldTypeEnum == null) {
            log.error("扩展字段类型异常：{}", fieldType);
            return null;
        }

        switch (fieldTypeEnum) {
            case NUMBER:
                return ConditionDataConverter.convertToDouble(stringValue);
            case DATETIME:
                return ConditionDataConverter.convertToDate(stringValue);
            default:
                return stringValue;
        }
    }

    /**
     * 填充 联系人标签
     *
     * @param labelParams
     * @param member
     */
    private void fillLabelValueId(List<MemberLabelParam> labelParams, Member member) {
        if (CollUtil.isNotEmpty(labelParams)) {
            List<LabelValueId> labels = labelParams.stream().map(f -> {
                LabelValueId labelValueId = new LabelValueId();
                labelValueId.setId(f.getId());
                labelValueId.setLabelId(f.getLabelId());
                return labelValueId;
            }).collect(Collectors.toList());
            member.setLabels(labels);
        }
    }

    /**
     * 填充 联系人标签
     *
     * @param labelParams
     * @param member
     */
    private void fillHttpLabelValueId(List<HttpMemberLabelParam> labelParams, Member member) {
        if (CollUtil.isNotEmpty(labelParams)) {
            List<LabelValueId> labels = labelParams.stream().map(f -> {
                LabelValueId labelValueId = new LabelValueId();
                labelValueId.setId(f.getId());
                labelValueId.setLabelId(f.getLabelId());
                return labelValueId;
            }).collect(Collectors.toList());
            member.setLabels(labels);
        }
    }

    /**
     * 获取组织信息
     *
     * @param orgId
     */
    public ServiceResult<OrganizationDTO> getOrganization(String orgId) {
        return xmMemberClient.getOrgByIdOrCode(orgId, null);
    }

    public ServiceResult<String> checkUpdateParam(MemberDO oldMember, HttpUpdateHttpMemberParam param) {
        //  邮箱
        String email = param.getEmail();
        boolean isEmail = StrUtil.isNotEmpty(email);
        // 手机号
        String mobile = param.getMobile();
        boolean isMobile = StrUtil.isNotEmpty(mobile);
        // 支付宝userid
        String aliPayUserId = param.getAlipayUserId();
        boolean isAliPayUserId = StrUtil.isNotEmpty(aliPayUserId);
        // 微信userid
        String openId = param.getOpenId();
        boolean isOpenId = StrUtil.isNotEmpty(openId);
        // 外部编号 内部联系人是 员工编号 其他类型的联系人是外部编号
        String outNo = param.getMemberType() == INTERIOR.getIndex() ? param.getEmployeeId() : param.getOutNo();
        String oldOutNo = oldMember.getMemberType() == INTERIOR.getIndex() ? oldMember.getEmployeeId() : oldMember.getOutNo();
        boolean isOutNo = StrUtil.isNotEmpty(outNo);
        // 个人联系人校验appid openid
        String appId = param.getAppId();
        boolean hasAppId = StrUtil.isNotEmpty(appId);
        if (!isEmail && !isMobile && !isOutNo) {
            if (INDIVIDUAL.matches(param.getMemberType())) {
                // AppID和OpenID必须同时存在
                if (!isOpenId || !hasAppId) {
                    String errorMessage = "手机号、邮箱、外部编号、微信appID+openID至少填写一个";
                    return ServiceResult.<String>fail(errorMessage).setErrorCode(String.valueOf(AppRequestErrorCode.PARAM_FORMAT));
                }
            } else {
                String errorMessage = param.getMemberType() == INTERIOR.getIndex() ? "用户手机号,邮箱,员工编号不能同时为空" : "用户手机号,邮箱,外部编号不能同时为空";
                return ServiceResult.<String>fail(errorMessage).setErrorCode(String.valueOf(AppRequestErrorCode.PARAM_FORMAT));
            }
        }
        if (isOpenId^hasAppId) {
            String errorMessage = "微信appID+openID必须同时存在";
            return ServiceResult.<String>fail(errorMessage).setErrorCode(String.valueOf(AppRequestErrorCode.PARAM_FORMAT));
        }
        if(StringUtils.hasText(appId) && StringUtils.hasText(openId)){
            if(!CheckUtil.checkAppIdFormat(appId, INDIVIDUAL.getIndex()).isSuccess()){
                return ServiceResult.fail("appID不能超过128位");
            }
            if(!CheckUtil.checkOpenIdFormat(openId, INDIVIDUAL.getIndex()).isSuccess()){
                return ServiceResult.fail("openID不能超过128位");
            }
        }
        // 对邮箱进行校验
        if (isEmail && !email.equals(oldMember.getEmail()) && param.getIsDisplayContact()) {
            ServiceResult<String> result = CheckUtil.checkEmailFormat(email);
            if (result.isSuccess()) {
                MemberDO member = memberDao.findByOrgIdAndFieldAndAttributeAndStatus(param.getOrgId(), "email", param.getEmail(), param.getMemberType(), NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
                if (ObjectUtil.isNotEmpty(member)) {
                    return ServiceResult.<String>fail(member.getId(), "邮箱号唯一，不能重复")
                            .setErrorCode(String.valueOf(AppResourceErrorCode.FOUND));
                }
            } else {
                return ServiceResult.<String>fail(result.getMsg()).setErrorCode("400");
            }
        }

        // 对手机号进行校验
        if (isMobile && !mobile.equals(oldMember.getPhone()) && param.getIsDisplayContact()) {
            ServiceResult<String> result = CheckUtil.checkMobilePhoneFormat(mobile);
            if (result.isSuccess()) {
                MemberDO member = memberDao.findByOrgIdAndFieldAndAttributeAndStatus(param.getOrgId(), "phone", param.getMobile(), param.getMemberType(), NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
                if (ObjectUtil.isNotEmpty(member)) {
                    return ServiceResult.<String>fail(member.getId(), "手机号值唯一，不能重复")
                            .setErrorCode(String.valueOf(AppResourceErrorCode.FOUND));
                }
            } else {
                return ServiceResult.<String>fail(result.getMsg()).setErrorCode("400");
            }
        }

        // 对外部编号进行校验
        if (isOutNo && !outNo.equals(oldOutNo)) {
            Integer memberType = param.getMemberType();
            ServiceResult<String> result = CheckUtil.checkOutNoFormat(outNo, memberType);
            if (result.isSuccess()) {
                MemberDO member = memberDao.findOneByOrgIdAndOutNoAndAttributeAndStatus(param.getOrgId(), outNo, param.getMemberType(), NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
                if (ObjectUtil.isNotEmpty(member)) {
                    String errorMessage = memberType == INTERIOR.getIndex() ? "员工编号唯一，不能重复" : "外部编号唯一，不能重复";
                    return ServiceResult.<String>fail(member.getId(), errorMessage)
                            .setErrorCode(String.valueOf(AppResourceErrorCode.FOUND));
                }
            } else {
                return ServiceResult.<String>fail(result.getMsg()).setErrorCode("400");
            }
        }

        if (isOpenId && hasAppId && (!openId.equals(oldMember.getOpenId()) || !appId.equals(oldMember.getAppId()))) {
            MemberDO member = memberDao.findByAppIdAndOpenId(param.getOrgId(), param.getMemberType(),
                    param.getAppId(), param.getOpenId());
            if (ObjectUtil.isNotEmpty(member)) {
                return ServiceResult.<String>fail(member.getId(), "该类型中，联系人appid和openid已存在")
                        .setErrorCode(String.valueOf(AppResourceErrorCode.FOUND));
            }
        }

        if (isAliPayUserId && !aliPayUserId.equals(oldMember.getAliPayUserId())) {
            MemberDO member = memberDao.findByOrgIdAndFieldAndAttributeAndStatus(param.getOrgId(), "ali_pay_user_id", param.getAlipayUserId(), param.getMemberType(), NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
            if (ObjectUtil.isNotEmpty(member)) {
                return ServiceResult.<String>fail(member.getId(), "支付宝ID值唯一，不能重复")
                        .setErrorCode(String.valueOf(AppResourceErrorCode.FOUND));
            }
        }
        return ServiceResult.success();
    }

    public List<MemberDO> getSimilarlyMembers(String orgId, String mobile,
                                              String email, String aliPayUserId,
                                              String openId, Integer mType, String outNo) {
        return memberDao.findByOrgIdAndMobileAndEmailAndAliPayUserIdAndOpenidAndAttribute(orgId, mobile, email,
                aliPayUserId, openId, outNo, mType, Arrays.asList(NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex()));
    }

    public ServiceResult<String> checkFieldValueFormatValid(List<HttpMemberFieldParam> fieldValues) {
        if (CollectionUtil.isNotEmpty(fieldValues)) {
            for (HttpMemberFieldParam field : fieldValues) {
                ServiceResult<String> result = FieldUtil.checkFieldValueFormat(field);
                if (!result.isSuccess()) {
                    return ServiceResult.fail(result.getMsg());
                }
            }
        }
        return ServiceResult.success();
    }

    /**
     * 联系人导入 更新
     *
     * @param member
     * @return
     */
    public ServiceResult<Void> updateImportMember(MemberDO member) {
        // 获取组织信息
        ServiceResult<OrganizationDTO> organization = getOrganization(member.getOrgId());
        if (!organization.isSuccess()) {
            return ServiceResult.fail(organization.getMsg());
        }
        member.setGroupSyncToken(null);
        memberDao.updateMember(member, false);
        OrganizationDTO data = organization.getData();
        // 推送更新的联系人
        MemberMessage message = MemberUtil.memberDoToMemberMessage(member);
        message.setOrgCode(data.getCode());
        message.setOrgName(data.getName());
        String identityType = "";
        switch (EnumUtil.getEnumByIndex(MemberTypeEnum.class, member.getMemberType())) {
            case INSTITUTION:
                message.setIdentityType("企业对接人");
                message.setNumberId("000");
                // 更新名片
                sourceCardDao.updateDataByMidAndSource((JSONObject) JSON.toJSON(member), member.getId(), 0, member.getOrgId());
                break;
            case INTERIOR:
                identityType = ObjectUtil.isNotEmpty(member.getEmployeeStatus()) ? EMPLOYEE_STATUS_MAP.get(member.getEmployeeStatus()) : "";
                message.setIdentityType(identityType);
                break;
            case INDIVIDUAL:
                identityType = memberUtil.getIdentityType(member.getPhone(), member.getOrgId(), member.getEmail());
                message.setIdentityType(identityType);
                // 更新名片
                sourceCardDao.updateDataByMidAndSource((JSONObject) JSON.toJSON(member), member.getId(), 0, member.getOrgId());
                break;
        }
        memberUtil.publishUpdateMember(message);
        return ServiceResult.success();
    }

    /**
     * 类型转换
     *
     * @param param
     * @return
     */
    protected MemberDO toMemberDoForUpdate(HttpMemberBaseParam param) {
        MemberDO memberDo = new MemberDO();
        memberDo.setSysId(param.getSysId());
        memberDo.setName(param.getName());
        memberDo.setNickname(param.getNickname());
        memberDo.setEmail(param.getEmail());
        memberDo.setPhone(param.getMobile());
        memberDo.setGender(param.getGender());
        memberDo.setStoreId(param.getStore());
        memberDo.setContent(param.getContent());
        memberDo.setOpenId(param.getOpenId());
        memberDo.setAppId(param.getAppId());
        memberDo.setUnionId(param.getUnionId());
        memberDo.setAvatar(param.getAvator());
        memberDo.setAliPayUserId(param.getAlipayUserId());
        memberDo.setAddress(JSONObject.toJSONString(param.getAddress()));
        memberDo.setEmployeeId(param.getEmployeeId());
        memberDo.setEmployeeStatus(param.getEmployeeStatus());
        memberDo.setMemberNo(param.getMemberNo());
        if (StrUtil.isNotEmpty(param.getBirthday())) {
            memberDo.setBirthday(cn.hutool.core.date.DateUtil.parse(param.getBirthday()).toJdkDate());
        }
        memberDo.setDepartment(param.getDepartment());
        memberDo.setPosition(param.getPosition());
        memberDo.setInstituteName(param.getInstituteName());
        memberDo.setInstituteId(param.getInstituteId());
        memberDo.setFieldValList(JSONObject.toJSONString(param.getFieldValues()));
        memberDo.setUserId(param.getUserId());
        memberDo.setAge(param.getAge());
        memberDo.setLabels(JSONObject.toJSONString(param.getLabels()));
        memberDo.setUpdateTime(new Date());
        memberDo.setRegion(param.getRegion());
        memberDo.setOutNo(param.getOutNo());
        return memberDo;
    }

    /**
     * 手动更新新联系人
     *
     * @param member
     * @param param
     * @return
     */
    public abstract XmResultJson<String> updateManualMember(MemberDO member,
                                                            HttpUpdateHttpMemberParam param);

    /**
     * 更新联系人
     *
     * @param member
     * @return
     */
    public void updateMember(MemberDO member) {
        memberDao.updateMember(member, false);
    }

    /**
     * 添加导入的联系人
     *
     * @param member
     * @return
     */
    public abstract ServiceResult<Void> saveImportMember(MemberDO member, Integer quota);
}
